package project;

import java.util.LinkedList;

public class multilevel 
{
	private int clk;
	private LinkedList<process>	allProcesses;
	private LinkedList<process> prio1;
	private LinkedList<process> prio2;
	private LinkedList<process> prio3;
	private LinkedList<process> prio4;
	private LinkedList<user>	users;
	private boolean inQuantum = false;

	
	/**
	 * Constructor for the multilevel scheduler
	 * @param clk
	 */
	public multilevel(int clk) {
		super();
		this.clk = clk;
		this.allProcesses	= new LinkedList<process>();
		this.prio1			= new LinkedList<process>();
		this.prio2			= new LinkedList<process>();
		this.prio3			= new LinkedList<process>();
		this.prio4			= new LinkedList<process>();
		this.users			= new LinkedList<user>();
	}

	/**
	 * Main Method to start our program
	 * @param args
	 */
	public static void main(String args[])
	{
		multilevel m = new multilevel(0);

		LinkedList<process> user1List = new LinkedList<process>();
		LinkedList<process> user2List = new LinkedList<process>();

		process p1 = new process("p1", 3, 4, "user1");
		user1List.add(p1);

		user user1 = new user("user1", user1List, 2);
		m.getUsers().addLast(user1);
		
		process p2 = new process("p2", 5, 2, "user2");
		user2List.add(p2);
		
		process p3 = new process("p3", 5, 2, "user2");
		user2List.add(p3);

		user user2 = new user("user2", user2List, 1);
		m.getUsers().addLast(user2);

		m.addingProcesses();
	}

	/**
	 * adding the process to run in prio1
	 */
	public void addingProcesses() 
	{
		//looping on all the users
		for (int i = 0; i < getUsers().size(); i++)
		{
			//looping on the processes of each user
			for (int j = 0; j < getUsers().get(i).getUserProcesses().size(); j++) 
			{
				int arrivalT = getUsers().get(i).getUserProcesses().get(j).getArrival_time();
				
				if (arrivalT == getClk())
				{
					getPrio1().addLast(getUsers().get(i).getUserProcesses().get(j));
					System.out.println("adding a process " + getUsers().get(i).getUserProcesses().get(j).getName());
				}
			}//end of for loop looping on the processes of a user
		}//end of for loop looping on the users
		
		removingRunningProcesses();
		removingEmptyUsers();
		
		//Added to ensure that no process cuts the quantum of the 
		//process currently running instead waits for its turn.
		if (!inQuantum)
		{
			run1();
		}
	}

	/**
	 * removes the processes that are running form the list
	 * of processes of a user
	 */
	public void removingRunningProcesses() 
	{
		for (int i = 0; i < getUsers().size(); i++) 
		{
			LinkedList<process> currentProcesses = getUsers().get(i).getUserProcesses();
			for (int j = 0; j < currentProcesses.size(); j++) 
			{
				if (getPrio1().contains(currentProcesses.get(j))) 
				{
					currentProcesses.remove(j);
				}
			}
		}
	}

	/**
	 * removes the users that have no more processes
	 */
	public void removingEmptyUsers() 
	{
		for (int i = 0; i < getUsers().size(); i++) 
		{
			if (getUsers().get(i).getUserProcesses().isEmpty())
			{
				getUsers().remove(i);
			}
		}
	}

	public void run1() 
	{
		System.out.println("now we are run1");

		if (!getPrio1().isEmpty()) 
		{
			if (getPrio1().get(0).getRun_time() >= 1)
			{
				System.out.println("we are now running "
						+ getPrio1().getFirst().getName());
				getPrio1().getFirst().setRun_time(
						getPrio1().getFirst().getRun_time() - 1);
				
				if (getPrio1().getFirst().getRun_time() != 0) 
				{
					getPrio2().addLast(getPrio1().getFirst());
				}

				getPrio1().remove(0);
				setClk(getClk() + 1);
				System.out.println("clk now is " + getClk());
			}
			addingProcesses();
		} 
		else
		{
		run2();
		}
	}

	public void run2()
	{
		System.out.println("now we are run2");

		if (!getPrio2().isEmpty())
		{
			inQuantum = true;
			System.out.println("we are now running "
					+ getPrio2().getFirst().getName());
			getPrio2().getFirst().setRun_time(
					getPrio2().getFirst().getRun_time() - 1);
			setClk(getClk() + 1);
			addingProcesses();
			System.out.println("clk now is " + getClk());
			// running it for the second time
			if (getPrio2().getFirst().getRun_time() > 0) 
			{
				System.out.println("we are now running "
						+ getPrio2().getFirst().getName());
				getPrio2().getFirst().setRun_time(
						getPrio2().getFirst().getRun_time() - 1);
				setClk(getClk() + 1);
				addingProcesses();
				System.out.println("clk now is " + getClk());
			}

			if (getPrio2().getFirst().getRun_time() != 0) 
			{
				getPrio3().addLast(getPrio2().getFirst());
			}

			getPrio2().remove(0);

			inQuantum = false;
			addingProcesses();
		}
		else
		{
			run3();
		}
	}

	public void run3() {
		System.out.println("now we are run3");

		boolean flag = false;
		if (!getPrio3().isEmpty()) {
			inQuantum = true;
			System.out.println("we are now running "
					+ getPrio3().getFirst().getName());
			getPrio3().getFirst().setRun_time(
					getPrio3().getFirst().getRun_time() - 1);
			setClk(getClk() + 1);
			System.out.println("clk now is " + getClk());
			addingProcesses();
			int counter = 1;
			while (!flag && counter < 4) {

				if (getPrio3().getFirst().getRun_time() > 0) {
					System.out.println("we are now running "
							+ getPrio3().getFirst().getName());
					getPrio3().getFirst().setRun_time(
							getPrio3().getFirst().getRun_time() - 1);
					setClk(getClk() + 1);
					System.out.println("clk now is " + getClk());
					addingProcesses();
					counter++;
				}
				if (getPrio3().getFirst().getRun_time() == 0) {
					flag = true;
				}

			}

			if (getPrio3().getFirst().getRun_time() != 0) {
				getPrio4().addLast(getPrio3().getFirst());
			}

			getPrio3().remove(0);

			inQuantum = false;
			addingProcesses();
		} else
			run4();
	}

	public void run4() {
		System.out.println("now we are run4");

		boolean flag = false;
		if (!getPrio4().isEmpty()) {
			inQuantum = true;
			System.out.println("we are now running "
					+ getPrio4().getFirst().getName());
			getPrio4().getFirst().setRun_time(
					getPrio4().getFirst().getRun_time() - 1);
			setClk(getClk() + 1);
			System.out.println("clk now is " + getClk());
			addingProcesses();
			int counter = 1;
			while (!flag && counter < 8) {

				if (getPrio4().getFirst().getRun_time() > 0) {
					System.out.println("we are now running "
							+ getPrio4().getFirst().getName());
					getPrio4().getFirst().setRun_time(
							getPrio4().getFirst().getRun_time() - 1);
					setClk(getClk() + 1);
					System.out.println("clk now is " + getClk());
					addingProcesses();
					counter++;
				}
				if (getPrio4().getFirst().getRun_time() == 0) {
					flag = true;
				}

			}

			if (getPrio4().getFirst().getRun_time() != 0) {
				getPrio4().addLast(getPrio4().getFirst());
			}

			getPrio4().remove(0);

			inQuantum = false;
			addingProcesses();
		} else {
			if (getUsers().isEmpty()) {
				return;
			}
			setClk(getClk() + 1);
			System.out.println("clk now is " + getClk());
			inQuantum = false;
			addingProcesses();
		}
	}

	//Setters and Getters for all the fields
	public int getClk() {
		return clk;
	}

	public void setClk(int clk) {
		this.clk = clk;
	}

	public LinkedList<process> getAllProcesses() {
		return allProcesses;
	}

	public void setAllProcesses(LinkedList<process> allProcesses) {
		this.allProcesses = allProcesses;
	}

	public LinkedList<user> getUsers() {
		return users;
	}

	public void setUsers(LinkedList<user> users) {
		this.users = users;
	}

	public LinkedList<process> getPrio1() {
		return prio1;
	}

	public void setPrio1(LinkedList<process> prio1) {
		this.prio1 = prio1;
	}

	public LinkedList<process> getPrio2() {
		return prio2;
	}

	public void setPrio2(LinkedList<process> prio2) {
		this.prio2 = prio2;
	}

	public LinkedList<process> getPrio3() {
		return prio3;
	}

	public void setPrio3(LinkedList<process> prio3) {
		this.prio3 = prio3;
	}

	public LinkedList<process> getPrio4() {
		return prio4;
	}

	public void setPrio4(LinkedList<process> prio4) {
		this.prio4 = prio4;
	}

	public boolean isInQuantum() {
		return inQuantum;
	}

	public void setInQuantum(boolean inQuantum) {
		this.inQuantum = inQuantum;
	}
	
	
}
